/*
 * ProtocoloPeer.cpp
 *
 *  Created on: Jun 16, 2009
 *      Author: joephantom
 */

#include "ProtocoloPeer.h"

ProtocoloPeer::ProtocoloPeer()
{
}

ProtocoloPeer::~ProtocoloPeer()
{
}

string ProtocoloPeer::mantenerVivo(){
	return string("");
}

string ProtocoloPeer::choke(){
	unsigned char c= 0;
	string respuesta;

	respuesta.append(1, c);

	return respuesta;
}

string ProtocoloPeer::unchoke(){
	unsigned char c= 1;
	string respuesta;

	respuesta.append(1, c);

	return respuesta;
}

string ProtocoloPeer::interesado(){
	unsigned char c= 2;
	string respuesta;

	respuesta.append(1, c);

	return respuesta;
}

string ProtocoloPeer::noInteresado(){
	unsigned char c= 3;
	string respuesta;

	respuesta.append(1, c);

	return respuesta;
}

string ProtocoloPeer::tengo(const unsigned long int indicePieza){
	char aux[4] = {0, 0, 0, 0};
	unsigned char c= 4;
	unsigned long int indice2 = htonl(indicePieza);
	string respuesta;

	respuesta.append(1, c);

	memcpy(aux, &indice2, 4);
	respuesta.append(aux, 4);

	return respuesta;
}

string ProtocoloPeer::bitfield(const list<unsigned long int> &piezas, const unsigned long int cantidadPiezas){
	unsigned char c= 5;
	string respuesta;

	//TODO transformar piezas en un string

	respuesta.append(1, c);
	respuesta += this->deBitfieldAstring(piezas, cantidadPiezas);

	return respuesta;
}

string ProtocoloPeer::pedido(const unsigned long int indicePieza, const unsigned long int offsetPieza, const unsigned long int longitud){
	char aux[4] = {0, 0, 0, 0};
	unsigned char c = 6;
	unsigned long int indice2 = htonl(indicePieza);
	unsigned long int principio2 = htonl(offsetPieza);
	unsigned long int longitud2 = htonl(longitud);
	string respuesta;

	respuesta.append(1, c);

	memcpy(aux, &indice2, 4);
	respuesta.append(aux, 4);
	aux[0] = 0;
	aux[1] = 0;
	aux[2] = 0;
	aux[3] = 0;

	memcpy(aux, &principio2, 4);
	respuesta.append(aux, 4);
	aux[0] = 0;
	aux[1] = 0;
	aux[2] = 0;
	aux[3] = 0;

	memcpy(aux, &longitud2, 4);
	respuesta.append(aux, 4);

	return respuesta;
}

string ProtocoloPeer::pieza(const unsigned long int indicePieza, const unsigned long int offsetPieza, const string &informacion){
	char aux[4] = {0, 0, 0, 0};
	unsigned char c = 7;
	unsigned long int indice2 = htonl(indicePieza);
	unsigned long int principio2 = htonl(offsetPieza);
	string respuesta;

	respuesta.append(1, c);

	memcpy(aux, &indice2, 4);
	respuesta.append(aux, 4);
	aux[0] = 0;
	aux[1] = 0;
	aux[2] = 0;
	aux[3] = 0;

	memcpy(aux, &principio2, 4);
	respuesta.append(aux, 4);
	aux[0] = 0;
	aux[1] = 0;
	aux[2] = 0;
	aux[3] = 0;

	respuesta += informacion; //TODO averiguar si se apendea bien la informacion a pesar de poder tener un \0 dentro de informacion

	return respuesta;
}

string ProtocoloPeer::cancelar(const unsigned long int indicePieza, const unsigned long int offsetPieza, const unsigned long int longitud){
	char aux[4];
	unsigned char c= 8;
	unsigned long int indice2 = htonl(indicePieza);
	unsigned long int principio2 = htonl(offsetPieza);
	unsigned long int longitud2 = htonl(longitud);
	string respuesta;

	respuesta.append(1, c);

	memcpy(aux, &indice2, 4);
	respuesta.append(aux);

	memcpy(aux, &principio2, 4);
	respuesta.append(aux);

	memcpy(aux, &longitud2, 4);
	respuesta.append(aux);

	return aux;
}

Respuesta* ProtocoloPeer::procesar(const string &s) const{
	string aux = s;
	unsigned char id = (unsigned char)s[0];

	if(s.size() == 0){
		return new Respuesta("mantener vivo");
	}

	switch (id) {
	case 0:
		return new Respuesta("choke");

	case 1:
		return new Respuesta("unchoke");

	case 2:
		return new Respuesta("interesado");

	case 3:
		return new Respuesta("no interesado");

	case 4:{
		unsigned long int idPieza = 0;

		memcpy(&idPieza, s.substr(1).c_str(), 4);

		idPieza = ntohl(idPieza);

		return new RespuestaTengo(idPieza);
	}
	case 5:
		return new RespuestaBitField(s.substr(1));

	case 6:{
		unsigned long int idPieza = 0;
		unsigned long int offsetBloque = 0;
		unsigned long int tamano = 0;

		memcpy(&idPieza, s.substr(1,4).c_str(), 4);
		idPieza = ntohl(idPieza);

		memcpy(&offsetBloque, s.substr(5,4).c_str(), 4);
		offsetBloque = ntohl(offsetBloque);

		memcpy(&tamano, s.substr(9,4).c_str(), 4);
		tamano = ntohl(tamano);

		return new RespuestaPedido(idPieza, offsetBloque, tamano);
	}
	case 7:{
		unsigned long int idPieza = 0;
		unsigned long int offsetBloque = 0;
		string bloque;

		memcpy(&idPieza, s.substr(1,4).c_str(), 4);
		idPieza = ntohl(idPieza);

		memcpy(&offsetBloque, s.substr(5,4).c_str(), 4);
		offsetBloque = ntohl(offsetBloque);

		bloque = s.substr(9);

		return new RespuestaBloque(idPieza, offsetBloque, bloque);
	}
	case 8:{
		unsigned long int idPieza = 0;
		unsigned long int offsetBloque = 0;
		unsigned long int tamano = 0;

		memcpy(&idPieza, s.substr(1,4).c_str(), 4);
		idPieza = ntohl(idPieza);

		memcpy(&offsetBloque, s.substr(5,4).c_str(), 4);
		offsetBloque = ntohl(offsetBloque);

		memcpy(&tamano, s.substr(9,4).c_str(), 4);
		tamano = ntohl(tamano);

		return new RespuestaCancelar(idPieza, offsetBloque, tamano);
	}
	default:
		return new Respuesta("id de mensaje no conocido");
		break;
	}

	return NULL;
}

string ProtocoloPeer::deBitfieldAstring(const list<unsigned long int> &lista, unsigned long int cantPiezas) const
{

	string aux = "";
	unsigned long int posActual = 0;
	list<unsigned long int>::const_iterator it = lista.begin();

	for ( ; it != lista.end(); it++) {
		unsigned long int piezaActual = *it;

		while (posActual < piezaActual) {
			aux += '0';
			posActual++;
		}

		posActual++;
		aux += '1';
	}

	while ((aux.size() % 8) != 0) {
		aux += '0';
	}

	string resultado;

	for ( unsigned int i = 0 ; i < aux.size() ; i+= 8){
		short int valorChar = 0;
		for ( int j = 0 ; j < 8 ; j++ ){
			if (aux[i+j] == '1'){
				valorChar += pow(2,7-j);
			}
		}
		unsigned char letraCorrespondiente = valorChar;
		resultado += letraCorrespondiente;
	}

	/* Se agregan los char nulos para completar la cadena */

	while (cantPiezas % 8 != 0 ) {
		cantPiezas++;
	}

	while ((resultado.size()*8) < cantPiezas) {
		resultado += '\0';
	}

	return resultado;
}
